<!DOCTYPE html>


<html lang="zh-CN">


<head>
  <meta charset="utf-8" />
    
  <meta name="description" content="迎着朝阳的博客" />
  
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
  <title>
    springboot集成springMVC原理 |  迎着朝阳
  </title>
  <meta name="generator" content="hexo-theme-ayer">
  
  <link rel="shortcut icon" href="https://dxysun.com/static/yan.png" />
  
  
<link rel="stylesheet" href="/dist/main.css">

  
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/Shen-Yu/cdn/css/remixicon.min.css">

  
<link rel="stylesheet" href="/css/custom.css">

  
  
<script src="https://cdn.jsdelivr.net/npm/pace-js@1.0.2/pace.min.js"></script>

  
  

  
<script>
var _hmt = _hmt || [];
(function() {
	var hm = document.createElement("script");
	hm.src = "https://hm.baidu.com/hm.js?aa994a8d65700b8835787dd39d079d7e";
	var s = document.getElementsByTagName("script")[0]; 
	s.parentNode.insertBefore(hm, s);
})();
</script>


</head>

</html>

<body>
  <div id="app">
    
      
    <main class="content on">
      <section class="outer">
  <article
  id="post-springmvcForSpringboot"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h1 class="article-title sea-center" style="border-left:0" itemprop="name">
  springboot集成springMVC原理
</h1>
 

    </header>
     
    <div class="article-meta">
      <a href="/2021/08/29/springmvcForSpringboot/" class="article-date">
  <time datetime="2021-08-29T02:24:02.000Z" itemprop="datePublished">2021-08-29</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/spring/">spring</a>
  </div>
  
<div class="word_count">
    <span class="post-time">
        <span class="post-meta-item-icon">
            <i class="ri-quill-pen-line"></i>
            <span class="post-meta-item-text"> 字数统计:</span>
            <span class="post-count">5k</span>
        </span>
    </span>

    <span class="post-time">
        &nbsp; | &nbsp;
        <span class="post-meta-item-icon">
            <i class="ri-book-open-line"></i>
            <span class="post-meta-item-text"> 阅读时长≈</span>
            <span class="post-count">24 分钟</span>
        </span>
    </span>
</div>
 
    </div>
      
    <div class="tocbot"></div>




  
    <div class="article-entry" itemprop="articleBody">
       
  <p>springboot集成springMVC原理</p>
<a id="more"></a>

<p>Springboot 十分流行，但是它是怎样集成springMVC 的呢，与之前springMVC 单独启动有什么不同呢？今天来探究一下</p>
<p>先了解一下servlet的演变</p>
<h1 id="servlet演变"><a href="#servlet演变" class="headerlink" title="servlet演变"></a>servlet演变</h1><h2 id="servlet3-0-以前的时代"><a href="#servlet3-0-以前的时代" class="headerlink" title="servlet3.0 以前的时代"></a>servlet3.0 以前的时代</h2><p>为了体现出整个演进过程，还是来回顾下 n 年前我们是怎么写 servlet 和 filter 代码的。</p>
<p>项目结构（本文都采用 maven 项目结构）</p>
<p><img src="https://tu.dxysun.com/image-20210829103841880-20210829103846.png" alt="image-20210829103841880"></p>
<p>HelloWorldServlet</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">HelloWorldServlet</span> <span class="keyword">extends</span> <span class="title">HttpServlet</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">doGet</span><span class="params">(HttpServletRequest req, HttpServletResponse resp)</span> <span class="keyword">throws</span> ServletException, IOException </span>&#123;</span><br><span class="line">        resp.setContentType(<span class="string">"text/plain"</span>);</span><br><span class="line">        PrintWriter out = resp.getWriter();</span><br><span class="line">        out.println(<span class="string">"hello world"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>HelloWorldFilter</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">HelloWorldFilter</span> <span class="keyword">implements</span> <span class="title">Filter</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">init</span><span class="params">(FilterConfig filterConfig)</span> <span class="keyword">throws</span> ServletException </span>&#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">doFilter</span><span class="params">(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)</span> <span class="keyword">throws</span> IOException, ServletException </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"触发 hello world 过滤器..."</span>);</span><br><span class="line">        filterChain.doFilter(servletRequest,servletResponse);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">destroy</span><span class="params">()</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p> web.xml </p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">web-app</span> <span class="attr">xmlns</span>=<span class="string">"http://java.sun.com/xml/ns/javaee"</span></span></span><br><span class="line"><span class="tag">         <span class="attr">xmlns:xsi</span>=<span class="string">"http://www.w3.org/2001/XMLSchema-instance"</span></span></span><br><span class="line"><span class="tag">         <span class="attr">xsi:schemaLocation</span>=<span class="string">"http://java.sun.com/xml/ns/javaee</span></span></span><br><span class="line"><span class="tag"><span class="string">        http://java.sun.com/xml/ns/javaee/web-app_4_0.xsd"</span></span></span><br><span class="line"><span class="tag">         <span class="attr">version</span>=<span class="string">"4.0"</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">servlet</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">servlet-name</span>&gt;</span>HelloWorldServlet<span class="tag">&lt;/<span class="name">servlet-name</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">servlet-class</span>&gt;</span>com.dxysun.servlet2x.servlet.HelloWorldServlet<span class="tag">&lt;/<span class="name">servlet-class</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">servlet</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">servlet-mapping</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">servlet-name</span>&gt;</span>HelloWorldServlet<span class="tag">&lt;/<span class="name">servlet-name</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">url-pattern</span>&gt;</span>/hello<span class="tag">&lt;/<span class="name">url-pattern</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">servlet-mapping</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">filter</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">filter-name</span>&gt;</span>HelloWorldFilter<span class="tag">&lt;/<span class="name">filter-name</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">filter-class</span>&gt;</span>com.dxysun.servlet2x.filter.HelloWorldFilter<span class="tag">&lt;/<span class="name">filter-class</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">filter</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">filter-mapping</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">filter-name</span>&gt;</span>HelloWorldFilter<span class="tag">&lt;/<span class="name">filter-name</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">url-pattern</span>&gt;</span>/hello<span class="tag">&lt;/<span class="name">url-pattern</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">filter-mapping</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">web-app</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>这样，一个 java web hello world 就完成了。</p>
<p>servlet3.0 以前我们需要配置 web.xml ，添加servlet 和 filter 的相关配置项</p>
<p>而在servlet3.0 之后我们就可以抛弃 web.xml 了</p>
<h2 id="servlet3-0-新特性"><a href="#servlet3-0-新特性" class="headerlink" title="servlet3.0 新特性"></a>servlet3.0 新特性</h2><p>Servlet 3.0 作为 Java EE 6 规范体系中一员，随着 Java EE 6 规范一起发布。该版本在前一版本（Servlet 2.5）的基础上提供了若干新特性用于简化 Web 应用的开发和部署。其中一项新特性便是提供了无 xml 配置的特性。</p>
<p>servlet3.0 首先提供了 @WebServlet，@WebFilter 等注解，这样便有了抛弃 web.xml 的第一个途径，凭借注解声明 servlet 和 filter 来做到这一点。</p>
<p>除了这种方式，servlet3.0 规范还提供了更强大的功能，可以在运行时动态注册 servlet ，filter，listener。</p>
<p>以 servlet 为例，过滤器与监听器与之类似。ServletContext 为动态配置 Servlet 增加了如下方法：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">ServletRegistration.<span class="function">Dynamic <span class="title">addServlet</span><span class="params">(String servletName,Class&lt;? extends Servlet&gt; servletClass)</span></span></span><br><span class="line"><span class="function">ServletRegistration.Dynamic <span class="title">addServlet</span><span class="params">(String servletName, Servlet servlet)</span></span></span><br><span class="line"><span class="function">ServletRegistration.Dynamic <span class="title">addServlet</span><span class="params">(String servletName, String className)</span></span></span><br><span class="line"><span class="function">T <span class="title">createServlet</span><span class="params">(Class clazz)</span></span></span><br><span class="line"><span class="function">ServletRegistration <span class="title">getServletRegistration</span><span class="params">(String servletName)</span></span></span><br><span class="line"><span class="function">Map&lt;String,? extends ServletRegistration&gt; <span class="title">getServletRegistrations</span><span class="params">()</span></span></span><br></pre></td></tr></table></figure>

<p>其中前三个方法的作用是相同的，只是参数类型不同而已；通过 createServlet()方法创建的 Servlet，通常需要做一些自定义的配置，然后使用 addServlet() 方法来将其动态注册为一个可以用于服务的 Servlet。两个 getServletRegistration() 方法主要用于动态为 Servlet 增加映射信息，这等价于在 web.xml 中使用 标签为存在的 Servlet 增加映射信息。</p>
<p>以上 ServletContext 新增的方法要么是在 ServletContextListener 的 contexInitialized 方法中调用，要么是在ServletContainerInitializer 的 onStartup() 方法中调用。</p>
<p>ServletContainerInitializer 也是 Servlet 3.0 新增的一个接口，容器在启动时使用 JAR 服务 API(JAR Service API) 来发现 </p>
<p>ServletContainerInitializer 的实现类，并且容器将 WEB-INF/lib 目录下 JAR 包中的类都交给该类的 onStartup()方法处理，我们通常需要在该实现类上使用 @HandlesTypes 注解来指定希望被处理的类，过滤掉不希望给 onStartup() 处理的类。</p>
<p>一个典型的 servlet3.0+ 的 web 项目结构如下：</p>
<p><img src="https://tu.dxysun.com/image-20210829104555889-20210829104557.png" alt="image-20210829104555889"></p>
<p>我们并未对 HelloWorldServlet 和 HelloWorldFilter 做任何改动，而是新增了一个 CustomServletContainerInitializer , 它实现了 <code>javax.servlet.ServletContainerInitializer</code> 接口，用来在 web 容器启动时加载指定的 servlet 和 filter，代码如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@HandlesTypes</span>(ApplicationInitializer<span class="class">.<span class="keyword">class</span>)</span></span><br><span class="line"><span class="class"><span class="title">public</span> <span class="title">class</span> <span class="title">CustomServletContainerInitializer</span> <span class="keyword">implements</span> <span class="title">ServletContainerInitializer</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">static</span> String JAR_HELLO_URL = <span class="string">"/hello"</span>;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Override</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onStartup</span><span class="params">(Set&lt;Class&lt;?&gt;&gt; c, ServletContext servletContext)</span></span></span><br><span class="line"><span class="function">	</span>&#123;</span><br><span class="line"></span><br><span class="line">		<span class="keyword">if</span> (c != <span class="keyword">null</span>)</span><br><span class="line">		&#123;</span><br><span class="line">			<span class="keyword">for</span> (Class&lt;?&gt; waiClass : c)</span><br><span class="line">			&#123;</span><br><span class="line">				<span class="keyword">if</span> (WebApplicationInitializer<span class="class">.<span class="keyword">class</span>.<span class="title">isAssignableFrom</span>(<span class="title">waiClass</span>))</span></span><br><span class="line"><span class="class">				</span>&#123;</span><br><span class="line">					<span class="keyword">try</span></span><br><span class="line">					&#123;</span><br><span class="line">						WebApplicationInitializer webApplicationInitializer = (WebApplicationInitializer) waiClass</span><br><span class="line">								.newInstance();</span><br><span class="line">						webApplicationInitializer.init();</span><br><span class="line">					&#125;</span><br><span class="line">					<span class="keyword">catch</span> (Throwable ex)</span><br><span class="line">					&#123;</span><br><span class="line">						<span class="keyword">throw</span> <span class="keyword">new</span> RuntimeException(<span class="string">"Failed to instantiate WebApplicationInitializer class"</span>, ex);</span><br><span class="line">					&#125;</span><br><span class="line">				&#125;</span><br><span class="line"></span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		System.out.println(<span class="string">"创建 helloWorldServlet..."</span>);</span><br><span class="line"></span><br><span class="line">		ServletRegistration.Dynamic servlet = servletContext.addServlet(HelloWorldServlet<span class="class">.<span class="keyword">class</span>.<span class="title">getSimpleName</span>(),</span></span><br><span class="line"><span class="class">				<span class="title">HelloWorldServlet</span>.<span class="title">class</span>)</span>;</span><br><span class="line">		servlet.addMapping(JAR_HELLO_URL);</span><br><span class="line"></span><br><span class="line">		System.out.println(<span class="string">"创建 helloWorldFilter..."</span>);</span><br><span class="line"></span><br><span class="line">		FilterRegistration.Dynamic filter = servletContext.addFilter(HelloWorldFilter<span class="class">.<span class="keyword">class</span>.<span class="title">getSimpleName</span>(),</span></span><br><span class="line"><span class="class">				<span class="title">HelloWorldFilter</span>.<span class="title">class</span>)</span>;</span><br><span class="line"></span><br><span class="line">		EnumSet&lt;DispatcherType&gt; dispatcherTypes = EnumSet.allOf(DispatcherType<span class="class">.<span class="keyword">class</span>)</span>;</span><br><span class="line">		dispatcherTypes.add(DispatcherType.REQUEST);</span><br><span class="line">		dispatcherTypes.add(DispatcherType.FORWARD);</span><br><span class="line"></span><br><span class="line">		filter.addMappingForUrlPatterns(dispatcherTypes, <span class="keyword">true</span>, JAR_HELLO_URL);</span><br><span class="line"></span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>ServletContext 我们称之为 servlet 上下文，它维护了整个 web 容器中注册的 servlet，filter，listener，以 servlet 为例，可以使用 servletContext.addServlet 等方法来添加 servlet。而方法入参中 <code>Set&lt;Class&gt; c</code> 和 <code>@HandlesTypes</code> 注解在 demo 中我自定义了一个接口，接口定义如下</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">ApplicationInitializer</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">init</span><span class="params">()</span> <span class="keyword">throws</span> ServletException</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>实现类如下</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">WebApplicationInitializer</span> <span class="keyword">implements</span> <span class="title">ApplicationInitializer</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">	<span class="meta">@Override</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">init</span><span class="params">()</span></span></span><br><span class="line"><span class="function">	</span>&#123;</span><br><span class="line">		System.out.println(<span class="string">"WebApplicationInitializer init"</span>);</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<blockquote>
<p>@HandlesTypes  可以指定接口，接口实现类会被传入<code>Set&lt;Class&gt; c</code>参数，也可以指定具体类，但只有继承该具体类的继承类才会被传入<code>Set&lt;Class&gt; c</code>参数，指定的具体类不会传进去</p>
</blockquote>
<p>正常的流程是使用 @HandlesTypes 指定需要处理的 class，继承了该class的类会被当做入参传入 <code>Set&lt;Class&gt; c</code>参数，而后可以对 <code>Set&lt;Class&gt; c</code>进行判断是否属于该 class，正如前文所言，onStartup 会加载不需要被处理的一些 class。</p>
<p>这么声明一个 ServletContainerInitializer 的实现类，web 容器并不会识别它，所以，需要借助 SPI 机制来指定该初始化类，这一步骤是通过在项目路径下创建 <code>META-INF/services/javax.servlet.ServletContainerInitializer</code> 来做到的，它只包含一行内容：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">com.dxysun.servlet3x.CustomServletContainerInitializer</span><br></pre></td></tr></table></figure>

<p>使用 ServletContainerInitializer 和 SPI 机制，我们的 web 应用便可以彻底摆脱 web.xml 了。</p>
<h1 id="Spring-是如何支持-servlet3-0-的？"><a href="#Spring-是如何支持-servlet3-0-的？" class="headerlink" title="Spring 是如何支持 servlet3.0 的？"></a>Spring 是如何支持 servlet3.0 的？</h1><p>spring 又是如何支持 servlet3.0 规范的呢？</p>
<p>寻找 spring 中 ServletContainerInitializer 的实现类并不困难，可以迅速定位到 SpringServletContainerInitializer 该实现类。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@HandlesTypes</span>(WebApplicationInitializer<span class="class">.<span class="keyword">class</span>)</span></span><br><span class="line"><span class="class"><span class="title">public</span> <span class="title">class</span> <span class="title">SpringServletContainerInitializer</span> <span class="keyword">implements</span> <span class="title">ServletContainerInitializer</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onStartup</span><span class="params">(Set&lt;Class&lt;?&gt;&gt; webAppInitializerClasses, ServletContext servletContext)</span></span></span><br><span class="line"><span class="function">			<span class="keyword">throws</span> ServletException </span>&#123;</span><br><span class="line"></span><br><span class="line">		List&lt;WebApplicationInitializer&gt; initializers = <span class="keyword">new</span> LinkedList&lt;WebApplicationInitializer&gt;();</span><br><span class="line"></span><br><span class="line">		<span class="keyword">if</span> (webAppInitializerClasses != <span class="keyword">null</span>) &#123;</span><br><span class="line">			<span class="keyword">for</span> (Class&lt;?&gt; waiClass : webAppInitializerClasses) &#123;</span><br><span class="line">				<span class="comment">// Be defensive: Some servlet containers provide us with invalid classes,</span></span><br><span class="line">				<span class="comment">// no matter what @HandlesTypes says...</span></span><br><span class="line">                <span class="comment">// &lt;1&gt;</span></span><br><span class="line">				<span class="keyword">if</span> (!waiClass.isInterface() &amp;&amp; !Modifier.isAbstract(waiClass.getModifiers()) &amp;&amp;</span><br><span class="line">						WebApplicationInitializer<span class="class">.<span class="keyword">class</span>.<span class="title">isAssignableFrom</span>(<span class="title">waiClass</span>)) </span>&#123;</span><br><span class="line">					<span class="keyword">try</span> &#123;</span><br><span class="line">						initializers.add((WebApplicationInitializer) waiClass.newInstance());</span><br><span class="line">					&#125;</span><br><span class="line">					<span class="keyword">catch</span> (Throwable ex) &#123;</span><br><span class="line">						<span class="keyword">throw</span> <span class="keyword">new</span> ServletException(<span class="string">"Failed to instantiate WebApplicationInitializer class"</span>, ex);</span><br><span class="line">					&#125;</span><br><span class="line">				&#125;</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		<span class="keyword">if</span> (initializers.isEmpty()) &#123;</span><br><span class="line">			servletContext.log(<span class="string">"No Spring WebApplicationInitializer types detected on classpath"</span>);</span><br><span class="line">			<span class="keyword">return</span>;</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		servletContext.log(initializers.size() + <span class="string">"Spring WebApplicationInitializers detected on classpath"</span>);</span><br><span class="line">		AnnotationAwareOrderComparator.sort(initializers);</span><br><span class="line">        <span class="comment">// &lt;2&gt;</span></span><br><span class="line">		<span class="keyword">for</span> (WebApplicationInitializer initializer : initializers) &#123;</span><br><span class="line">			initializer.onStartup(servletContext);</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>&lt;1&gt; 英文注释是 spring 源码中自带的，它提示我们由于 servlet 厂商实现的差异，onStartup 方法会加载我们本不想处理的 class，所以进行了特判。</p>
<p>&lt;2&gt; spring 与之前的 demo 不同，并没有在 SpringServletContainerInitializer 中直接对 servlet 和 filter 进行注册，而是委托给了一个陌生的类 WebApplicationInitializer ，WebApplicationInitializer 类便是 spring 用来初始化 web 环境的委托者类，它通常有三个实现类：</p>
<p><img src="https://tu.dxysun.com/image-20210829134420628-20210829134421.png" alt="image-20210829134420628"></p>
<p>AbstractDispatcherServletInitializer#registerDispatcherServlet 便是无 web.xml 前提下创建 dispatcherServlet 的关键代码。</p>
<p>可以去项目中寻找一下 org.springframework:spring-web:version 的依赖，它下面就存在一个 servletContainerInitializer 的扩展，指向了 SpringServletContainerInitializer，这样只要在 servlet3.0 环境下部署，spring 便可以自动加载进行初始化：</p>
<p><img src="https://tu.dxysun.com/image-20210829134850532-20210829134851.png" alt="image-20210829134850532"></p>
<p>注意，上述这一切特性从 spring 3 就已经存在了，而如今 spring 5 已经伴随 springboot 2.0 一起发行了。</p>
<h1 id="SpringBoot-如何加载-Servlet"><a href="#SpringBoot-如何加载-Servlet" class="headerlink" title="SpringBoot 如何加载 Servlet"></a>SpringBoot 如何加载 Servlet</h1><p>springboot加载servlet并没有遵循servlet规范。</p>
<p>前面所讲述的 servlet 的规范，无论是 web.xml 中的配置，还是 servlet3.0 中的 ServletContainerInitializer 和 springboot 的加载流程都没有太大的关联。</p>
<p>先看看如何在 springboot 中注册 servlet 和 filter。</p>
<h2 id="注册-servlet-和-filter"><a href="#注册-servlet-和-filter" class="headerlink" title="注册 servlet 和 filter"></a>注册 servlet 和 filter</h2><h3 id="注册方式一：servlet3-0-注解-ServletComponentScan"><a href="#注册方式一：servlet3-0-注解-ServletComponentScan" class="headerlink" title="注册方式一：servlet3.0 注解 +@ServletComponentScan"></a>注册方式一：servlet3.0 注解 +@ServletComponentScan</h3><p>springboot 依旧兼容 servlet3.0 一系列以 @Web* 开头的注解：@WebServlet，@WebFilter，@WebListener</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@WebServlet</span>(<span class="string">"/hello"</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">HelloWorldServlet</span> <span class="keyword">extends</span> <span class="title">HttpServlet</span></span>&#123;&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@WebFilter</span>(<span class="string">"/hello/*"</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">HelloWorldFilter</span> <span class="keyword">implements</span> <span class="title">Filter</span> </span>&#123;&#125;</span><br></pre></td></tr></table></figure>

<p>让启动类去扫描到这些注解</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="meta">@ServletComponentScan</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SpringBootServletApplication</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">   <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">      SpringApplication.run(SpringBootServletApplication<span class="class">.<span class="keyword">class</span>, <span class="title">args</span>)</span>;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure>

<h3 id="注册方式二：RegistrationBean"><a href="#注册方式二：RegistrationBean" class="headerlink" title="注册方式二：RegistrationBean"></a>注册方式二：RegistrationBean</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Bean</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> ServletRegistrationBean <span class="title">helloWorldServlet</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    ServletRegistrationBean helloWorldServlet = <span class="keyword">new</span> ServletRegistrationBean();</span><br><span class="line">    myServlet.addUrlMappings(<span class="string">"/hello"</span>);</span><br><span class="line">    myServlet.setServlet(<span class="keyword">new</span> HelloWorldServlet());</span><br><span class="line">    <span class="keyword">return</span> helloWorldServlet;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Bean</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> FilterRegistrationBean <span class="title">helloWorldFilter</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    FilterRegistrationBean helloWorldFilter = <span class="keyword">new</span> FilterRegistrationBean();</span><br><span class="line">    myFilter.addUrlPatterns(<span class="string">"/hello/*"</span>);</span><br><span class="line">    myFilter.setFilter(<span class="keyword">new</span> HelloWorldFilter());</span><br><span class="line">    <span class="keyword">return</span> helloWorldFilter;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>ServletRegistrationBean 和 FilterRegistrationBean 都集成自 RegistrationBean ，RegistrationBean 是 springboot 中广泛应用的一个注册类，负责把 servlet，filter，listener 给容器化，使他们被 spring 托管，并且完成自身对 web 容器的注册。这种注册方式也值得推崇。</p>
<p><img src="https://tu.dxysun.com/image-20210829142239405-20210829142245.png" alt="image-20210829142239405"></p>
<p>从图中可以看出 RegistrationBean 的地位，它的几个实现类作用分别是：帮助容器注册 filter，servlet，listener，最后的 DelegatingFilterProxyRegistrationBean 使用的不多。另外 RegistrationBean 实现了 ServletContextInitializer 接口，这个接口将会是下面分析的核心接口。</p>
<h2 id="SpringBoot-中-servlet-加载流程的源码分析"><a href="#SpringBoot-中-servlet-加载流程的源码分析" class="headerlink" title="SpringBoot 中 servlet 加载流程的源码分析"></a>SpringBoot 中 servlet 加载流程的源码分析</h2><p>为什么说 springboot 没有完全遵守 servlet3.0 规范。讨论的前提是 springboot 环境下使用内嵌的容器，比如最典型的 tomcat。</p>
<h3 id="Initializer-被替换为-TomcatStarter"><a href="#Initializer-被替换为-TomcatStarter" class="headerlink" title="Initializer 被替换为 TomcatStarter"></a>Initializer 被替换为 TomcatStarter</h3><p>当使用内嵌的 tomcat 时，会发现 springboot 完全走了另一套初始化流程，完全没有使用前面提到的 SpringServletContainerInitializer，而是使用了 TomcatStarter 这个类，这两个类都实现了ServletContainerInitializer这个接口。</p>
<p><img src="https://tu.dxysun.com/image-20210829142720661-20210829142721.png" alt="image-20210829142720661"></p>
<p>但是有 没有SPI 文件对应到 TomcatStarter。所以内嵌 tomcat 的加载不依赖于 servlet3.0 规范和 SPI！它完全走了一套独立的逻辑。</p>
<p><a href="https://github.com/spring-projects/spring-boot/issues/321" target="_blank" rel="noopener">https://github.com/spring-projects/spring-boot/issues/321</a>  ，这里是作者的回答。</p>
<p>springboot 这么做是有意而为之。springboot 考虑到了如下的问题，在使用 springboot 时，开发阶段一般都是使用内嵌 tomcat 容器，但部署时却存在两种选择：一种是打成 jar 包，使用 java -jar 的方式运行；另一种是打成 war 包，交给外置容器去运行。前者就会导致容器搜索算法出现问题，因为这是 jar 包的运行策略，不会按照 servlet3.0 的策略去加载 ServletContainerInitializer！最后作者提供了一个替代选项：ServletContextInitializer，注意是 ServletContextInitializer！它和 ServletContainerInitializer 长得特别像，别搞混淆了，前者 ServletContextInitializer 是 org.springframework.boot.web.servlet.ServletContextInitializer，后者 ServletContainerInitializer 是 javax.servlet.ServletContainerInitializer，前文还提到 RegistrationBean 实现了 ServletContextInitializer 接口。</p>
<h3 id="TomcatStarter-中的-ServletContextInitializer-是关键"><a href="#TomcatStarter-中的-ServletContextInitializer-是关键" class="headerlink" title="TomcatStarter 中的 ServletContextInitializer 是关键"></a>TomcatStarter 中的 ServletContextInitializer 是关键</h3><p>TomcatStarter 中的 <code>org.springframework.boot.context.embedded.ServletContextInitializer</code> 是 springboot 初始化 servlet，filter，listener 的关键。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">TomcatStarter</span> <span class="keyword">implements</span> <span class="title">ServletContainerInitializer</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">private</span> <span class="keyword">final</span> ServletContextInitializer[] initializers;</span><br><span class="line"></span><br><span class="line">   TomcatStarter(ServletContextInitializer[] initializers) &#123;</span><br><span class="line">      <span class="keyword">this</span>.initializers = initializers;</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="meta">@Override</span></span><br><span class="line">   <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onStartup</span><span class="params">(Set&lt;Class&lt;?&gt;&gt; classes, ServletContext servletContext)</span></span></span><br><span class="line"><span class="function">         <span class="keyword">throws</span> ServletException </span>&#123;</span><br><span class="line">         <span class="keyword">for</span> (ServletContextInitializer initializer : <span class="keyword">this</span>.initializers) &#123;</span><br><span class="line">            initializer.onStartup(servletContext);</span><br><span class="line">         &#125;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>可以看出 TomcatStarter 的主要逻辑，它其实就是负责调用一系列 ServletContextInitializer 的 onStartup 方法。</p>
<p>ServletContextInitializer[] initializers 到底包含了哪些类呢？会不会有我们前面介绍的 RegisterBean 呢？</p>
<p><img src="https://tu.dxysun.com/image-20210829144045291-20210829144046.png" alt="image-20210829144045291"></p>
<p>RegisterBean 并没有出现在 TomcatStarter 的 debug 信息中，initializers 只包含了三个类，其中只有最后一个类看上去比较核心，注意这个类不是 ServletWebServerApplicationContext！而是这个类中的 $1 匿名类，进一步debug对应其selfInitialize这个方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">selfInitialize</span><span class="params">(ServletContext servletContext)</span> <span class="keyword">throws</span> ServletException </span>&#123;</span><br><span class="line">	prepareWebApplicationContext(servletContext);</span><br><span class="line">	registerApplicationScope(servletContext);</span><br><span class="line">	WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);</span><br><span class="line">	<span class="keyword">for</span> (ServletContextInitializer beans : getServletContextInitializerBeans()) &#123;</span><br><span class="line">		beans.onStartup(servletContext);</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>那么问题来了 ServletWebServerApplicationContext的selfInitialize是何时放到initializers的数组里呢？</p>
<h3 id="ServletWebServerApplicationContext"><a href="#ServletWebServerApplicationContext" class="headerlink" title="ServletWebServerApplicationContext"></a>ServletWebServerApplicationContext</h3><p>我们从springboot的启动入手，AnnotationConfigServletWebServerApplicationContext 继承了ServletWebServerApplicationContext，springboot而springboot在servlet环境中默认加载的ApplicationContext就是AnnotationConfigServletWebServerApplicationContext</p>
<p>org.springframework.boot.SpringApplication#run(java.lang.String…) 方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> ConfigurableApplicationContext <span class="title">run</span><span class="params">(String... args)</span> </span>&#123;</span><br><span class="line">	StopWatch stopWatch = <span class="keyword">new</span> StopWatch();</span><br><span class="line">	stopWatch.start();</span><br><span class="line">	ConfigurableApplicationContext context = <span class="keyword">null</span>;</span><br><span class="line">	configureHeadlessProperty();</span><br><span class="line">	SpringApplicationRunListeners listeners = getRunListeners(args);</span><br><span class="line">	listeners.starting();</span><br><span class="line">	<span class="keyword">try</span> &#123;</span><br><span class="line">		ApplicationArguments applicationArguments = <span class="keyword">new</span> DefaultApplicationArguments(args);</span><br><span class="line">		ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);</span><br><span class="line">		configureIgnoreBeanInfo(environment);</span><br><span class="line">		Banner printedBanner = printBanner(environment);</span><br><span class="line">     <span class="comment">// 这个地方创建的ApplicationContext就是AnnotationConfigServletWebServerApplicationContext</span></span><br><span class="line">		context = createApplicationContext();</span><br><span class="line">		prepareContext(context, environment, listeners, applicationArguments, printedBanner);</span><br><span class="line">		<span class="comment">// 刷新加载ApplicationContext，也就是创建ioc容器</span></span><br><span class="line">     refreshContext(context);</span><br><span class="line">		afterRefresh(context, applicationArguments);</span><br><span class="line">		stopWatch.stop();</span><br><span class="line">		<span class="keyword">if</span> (<span class="keyword">this</span>.logStartupInfo) &#123;</span><br><span class="line">			<span class="keyword">new</span> StartupInfoLogger(<span class="keyword">this</span>.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);</span><br><span class="line">		&#125;</span><br><span class="line">		listeners.started(context);</span><br><span class="line">		callRunners(context, applicationArguments);</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">catch</span> (Throwable ex) &#123;</span><br><span class="line">		handleRunFailure(context, ex, listeners);</span><br><span class="line">		<span class="keyword">throw</span> <span class="keyword">new</span> IllegalStateException(ex);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">try</span> &#123;</span><br><span class="line">		listeners.running(context);</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">catch</span> (Throwable ex) &#123;</span><br><span class="line">		handleRunFailure(context, ex, <span class="keyword">null</span>);</span><br><span class="line">		<span class="keyword">throw</span> <span class="keyword">new</span> IllegalStateException(ex);</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">return</span> context;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>进入refreshContext</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">	<span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">refreshContext</span><span class="params">(ConfigurableApplicationContext context)</span> </span>&#123;</span><br><span class="line">		<span class="keyword">if</span> (<span class="keyword">this</span>.registerShutdownHook) &#123;</span><br><span class="line">			<span class="keyword">try</span> &#123;</span><br><span class="line">				context.registerShutdownHook();</span><br><span class="line">			&#125;</span><br><span class="line">			<span class="keyword">catch</span> (AccessControlException ex) &#123;</span><br><span class="line">				<span class="comment">// Not allowed in some environments.</span></span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">		refresh((ApplicationContext) context);</span><br><span class="line">	&#125;</span><br><span class="line">...</span><br><span class="line">  	<span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">refresh</span><span class="params">(ConfigurableApplicationContext applicationContext)</span> </span>&#123;</span><br><span class="line">		applicationContext.refresh();</span><br><span class="line">	&#125;</span><br></pre></td></tr></table></figure>

<blockquote>
<p>refresh 一路走进去，最后到<code>applicationContext.refresh();</code></p>
</blockquote>
<p>因为AnnotationConfigServletWebServerApplicationContext 继承了ServletWebServerApplicationContext，最后会进入到<code>ServletWebServerApplicationContext#refresh()</code>方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">refresh</span><span class="params">()</span> <span class="keyword">throws</span> BeansException, IllegalStateException </span>&#123;</span><br><span class="line">	<span class="keyword">try</span> &#123;</span><br><span class="line">     <span class="comment">// 调用父类org.springframework.context.support.AbstractApplicationContext的refresh()</span></span><br><span class="line">		<span class="keyword">super</span>.refresh();</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">catch</span> (RuntimeException ex) &#123;</span><br><span class="line">		WebServer webServer = <span class="keyword">this</span>.webServer;</span><br><span class="line">		<span class="keyword">if</span> (webServer != <span class="keyword">null</span>) &#123;</span><br><span class="line">			webServer.stop();</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">throw</span> ex;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>父类org.springframework.context.support.AbstractApplicationContext的refresh()如下</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">refresh</span><span class="params">()</span> <span class="keyword">throws</span> BeansException, IllegalStateException </span>&#123;</span><br><span class="line">	<span class="keyword">synchronized</span> (<span class="keyword">this</span>.startupShutdownMonitor) &#123;</span><br><span class="line">		<span class="comment">// Prepare this context for refreshing.</span></span><br><span class="line">		prepareRefresh();</span><br><span class="line"></span><br><span class="line">		<span class="comment">// Tell the subclass to refresh the internal bean factory.</span></span><br><span class="line">		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();</span><br><span class="line"></span><br><span class="line">		<span class="comment">// Prepare the bean factory for use in this context.</span></span><br><span class="line">		prepareBeanFactory(beanFactory);</span><br><span class="line"></span><br><span class="line">		<span class="keyword">try</span> &#123;</span><br><span class="line">			<span class="comment">// Allows post-processing of the bean factory in context subclasses.</span></span><br><span class="line">			postProcessBeanFactory(beanFactory);</span><br><span class="line"></span><br><span class="line">			<span class="comment">// Invoke factory processors registered as beans in the context.</span></span><br><span class="line">			invokeBeanFactoryPostProcessors(beanFactory);</span><br><span class="line"></span><br><span class="line">			<span class="comment">// Register bean processors that intercept bean creation.</span></span><br><span class="line">			registerBeanPostProcessors(beanFactory);</span><br><span class="line"></span><br><span class="line">			<span class="comment">// Initialize message source for this context.</span></span><br><span class="line">			initMessageSource();</span><br><span class="line"></span><br><span class="line">			<span class="comment">// Initialize event multicaster for this context.</span></span><br><span class="line">			initApplicationEventMulticaster();</span><br><span class="line"></span><br><span class="line">			<span class="comment">// Initialize other special beans in specific context subclasses.</span></span><br><span class="line">			onRefresh();</span><br><span class="line"></span><br><span class="line">			<span class="comment">// Check for listener beans and register them.</span></span><br><span class="line">			registerListeners();</span><br><span class="line"></span><br><span class="line">			<span class="comment">// Instantiate all remaining (non-lazy-init) singletons.</span></span><br><span class="line">			finishBeanFactoryInitialization(beanFactory);</span><br><span class="line"></span><br><span class="line">			<span class="comment">// Last step: publish corresponding event.</span></span><br><span class="line">			finishRefresh();</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		<span class="keyword">catch</span> (BeansException ex) &#123;</span><br><span class="line">			<span class="keyword">if</span> (logger.isWarnEnabled()) &#123;</span><br><span class="line">				logger.warn(<span class="string">"Exception encountered during context initialization - "</span> +</span><br><span class="line">						<span class="string">"cancelling refresh attempt: "</span> + ex);</span><br><span class="line">			&#125;</span><br><span class="line"></span><br><span class="line">			<span class="comment">// Destroy already created singletons to avoid dangling resources.</span></span><br><span class="line">			destroyBeans();</span><br><span class="line"></span><br><span class="line">			<span class="comment">// Reset 'active' flag.</span></span><br><span class="line">			cancelRefresh(ex);</span><br><span class="line"></span><br><span class="line">			<span class="comment">// Propagate exception to caller.</span></span><br><span class="line">			<span class="keyword">throw</span> ex;</span><br><span class="line">		&#125;</span><br><span class="line"></span><br><span class="line">		<span class="keyword">finally</span> &#123;</span><br><span class="line">			<span class="comment">// Reset common introspection caches in Spring's core, since we</span></span><br><span class="line">			<span class="comment">// might not ever need metadata for singleton beans anymore...</span></span><br><span class="line">			resetCommonCaches();</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<blockquote>
<p>这个方法是spring IOC容器的加载过程，这里不再过多深入，我们只要关注<code>onRefresh();</code>方法，这个会调用子类的onRefresh方法</p>
</blockquote>
<p>在这里 onRefresh() 会调用ServletWebServerApplicationContext#onRefresh()方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onRefresh</span><span class="params">()</span> </span>&#123;</span><br><span class="line">	<span class="keyword">super</span>.onRefresh();</span><br><span class="line">	<span class="keyword">try</span> &#123;</span><br><span class="line">     <span class="comment">// 关键方法，创建了内嵌的 servlet 容器</span></span><br><span class="line">		createWebServer();</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">catch</span> (Throwable ex) &#123;</span><br><span class="line">		<span class="keyword">throw</span> <span class="keyword">new</span> ApplicationContextException(<span class="string">"Unable to start web server"</span>, ex);</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>createWebServer()</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">createWebServer</span><span class="params">()</span> </span>&#123;</span><br><span class="line">	WebServer webServer = <span class="keyword">this</span>.webServer;</span><br><span class="line">	ServletContext servletContext = getServletContext();</span><br><span class="line">	<span class="keyword">if</span> (webServer == <span class="keyword">null</span> &amp;&amp; servletContext == <span class="keyword">null</span>) &#123;</span><br><span class="line">		ServletWebServerFactory factory = getWebServerFactory();</span><br><span class="line">     <span class="comment">// 此处的factory是TomcatServletWebServerFactory</span></span><br><span class="line">		<span class="keyword">this</span>.webServer = factory.getWebServer(getSelfInitializer());</span><br><span class="line">		getBeanFactory().registerSingleton(<span class="string">"webServerGracefulShutdown"</span>,</span><br><span class="line">				<span class="keyword">new</span> WebServerGracefulShutdownLifecycle(<span class="keyword">this</span>.webServer));</span><br><span class="line">		getBeanFactory().registerSingleton(<span class="string">"webServerStartStop"</span>,</span><br><span class="line">				<span class="keyword">new</span> WebServerStartStopLifecycle(<span class="keyword">this</span>, <span class="keyword">this</span>.webServer));</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">else</span> <span class="keyword">if</span> (servletContext != <span class="keyword">null</span>) &#123;</span><br><span class="line">		<span class="keyword">try</span> &#123;</span><br><span class="line">			getSelfInitializer().onStartup(servletContext);</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">catch</span> (ServletException ex) &#123;</span><br><span class="line">			<span class="keyword">throw</span> <span class="keyword">new</span> ApplicationContextException(<span class="string">"Cannot initialize servlet context"</span>, ex);</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">	initPropertySources();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>this.webServer = factory.getWebServer(getSelfInitializer());</code>关键代码在这一行，factory此时为TomcatServletWebServerFactory，至于为什么是TomcatServletWebServerFactory，后面再介绍。</p>
<p><code>getSelfInitializer()</code> 便涉及到了我们最为关心的初始化流程。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> org.springframework.boot.web.servlet.<span class="function">ServletContextInitializer <span class="title">getSelfInitializer</span><span class="params">()</span> </span>&#123;</span><br><span class="line">	<span class="keyword">return</span> <span class="keyword">this</span>::selfInitialize;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">selfInitialize</span><span class="params">(ServletContext servletContext)</span> <span class="keyword">throws</span> ServletException </span>&#123;</span><br><span class="line">	prepareWebApplicationContext(servletContext);</span><br><span class="line">	registerApplicationScope(servletContext);</span><br><span class="line">	WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);</span><br><span class="line">	<span class="keyword">for</span> (ServletContextInitializer beans : getServletContextInitializerBeans()) &#123;</span><br><span class="line">		beans.onStartup(servletContext);</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>还记得前面 TomcatStarter 的 debug 信息中，最后一个 ServletContextInitializer 就是出现在 ServletWebServerApplicationContext 中的一个匿名类，没错了，就是这里的 getSelfInitializer()方法创建的！</p>
<p>解释下这里的 getSelfInitializer() 和 selfInitialize(ServletContext servletContext) 为什么要这么设计：这是典型的回调式方式，当匿名 ServletContextInitializer 类被 TomcatStarter 的 onStartup 方法调用，设计上是触发了 selfInitialize(ServletContext servletContext) 的调用。</p>
<p>所以这下就清晰了，为什么 TomcatStarter 中没有出现 RegisterBean ，其实是隐式触发了 ServletWebServerApplicationContext 中的 selfInitialize 方法。selfInitialize 方法中的 getServletContextInitializerBeans() 成了关键。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Returns &#123;<span class="doctag">@link</span> ServletContextInitializer&#125;s that should be used with the embedded</span></span><br><span class="line"><span class="comment"> * Servlet context. By default this method will first attempt to find</span></span><br><span class="line"><span class="comment"> * &#123;<span class="doctag">@link</span> ServletContextInitializer&#125;, &#123;<span class="doctag">@link</span> Servlet&#125;, &#123;<span class="doctag">@link</span> Filter&#125; and certain</span></span><br><span class="line"><span class="comment"> * &#123;<span class="doctag">@link</span> EventListener&#125; beans.</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> the servlet initializer beans</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> Collection&lt;ServletContextInitializer&gt; <span class="title">getServletContextInitializerBeans</span><span class="params">()</span> </span>&#123;</span><br><span class="line">		<span class="keyword">return</span> <span class="keyword">new</span> ServletContextInitializerBeans(getBeanFactory());</span><br><span class="line">	&#125;</span><br></pre></td></tr></table></figure>
<p>注释都告诉我们，这个 ServletContextInitializerBeans 是用来加载 Servlet 和 Filter 的。</p>
<p>ServletContextInitializerBeans 的构造方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@SafeVarargs</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">ServletContextInitializerBeans</span><span class="params">(ListableBeanFactory beanFactory,</span></span></span><br><span class="line"><span class="function"><span class="params">		Class&lt;? extends ServletContextInitializer&gt;... initializerTypes)</span> </span>&#123;</span><br><span class="line">	<span class="keyword">this</span>.initializers = <span class="keyword">new</span> LinkedMultiValueMap&lt;&gt;();</span><br><span class="line">	<span class="keyword">this</span>.initializerTypes = (initializerTypes.length != <span class="number">0</span>) ? Arrays.asList(initializerTypes)</span><br><span class="line">			: Collections.singletonList(ServletContextInitializer<span class="class">.<span class="keyword">class</span>)</span>;</span><br><span class="line">	addServletContextInitializerBeans(beanFactory);</span><br><span class="line">	addAdaptableBeans(beanFactory);</span><br><span class="line">	List&lt;ServletContextInitializer&gt; sortedInitializers = <span class="keyword">this</span>.initializers.values().stream()</span><br><span class="line">			.flatMap((value) -&gt; value.stream().sorted(AnnotationAwareOrderComparator.INSTANCE))</span><br><span class="line">			.collect(Collectors.toList());</span><br><span class="line">	<span class="keyword">this</span>.sortedList = Collections.unmodifiableList(sortedInitializers);</span><br><span class="line">	logMappings(<span class="keyword">this</span>.initializers);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>进入<code>addServletContextInitializerBeans(beanFactory)</code></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">addServletContextInitializerBeans</span><span class="params">(ListableBeanFactory beanFactory)</span> </span>&#123;</span><br><span class="line">	<span class="keyword">for</span> (Class&lt;? extends ServletContextInitializer&gt; initializerType : <span class="keyword">this</span>.initializerTypes) &#123;</span><br><span class="line">		<span class="keyword">for</span> (Entry&lt;String, ? extends ServletContextInitializer&gt; initializerBean : getOrderedBeansOfType(beanFactory,</span><br><span class="line">				initializerType)) &#123;</span><br><span class="line">			addServletContextInitializerBean(initializerBean.getKey(), initializerBean.getValue(), beanFactory);</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">addServletContextInitializerBean</span><span class="params">(String beanName, ServletContextInitializer initializer,</span></span></span><br><span class="line"><span class="function"><span class="params">		ListableBeanFactory beanFactory)</span> </span>&#123;</span><br><span class="line">	<span class="keyword">if</span> (initializer <span class="keyword">instanceof</span> ServletRegistrationBean) &#123;</span><br><span class="line">		Servlet source = ((ServletRegistrationBean&lt;?&gt;) initializer).getServlet();</span><br><span class="line">		addServletContextInitializerBean(Servlet<span class="class">.<span class="keyword">class</span>, <span class="title">beanName</span>, <span class="title">initializer</span>, <span class="title">beanFactory</span>, <span class="title">source</span>)</span>;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">else</span> <span class="keyword">if</span> (initializer <span class="keyword">instanceof</span> FilterRegistrationBean) &#123;</span><br><span class="line">		Filter source = ((FilterRegistrationBean&lt;?&gt;) initializer).getFilter();</span><br><span class="line">		addServletContextInitializerBean(Filter<span class="class">.<span class="keyword">class</span>, <span class="title">beanName</span>, <span class="title">initializer</span>, <span class="title">beanFactory</span>, <span class="title">source</span>)</span>;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">else</span> <span class="keyword">if</span> (initializer <span class="keyword">instanceof</span> DelegatingFilterProxyRegistrationBean) &#123;</span><br><span class="line">		String source = ((DelegatingFilterProxyRegistrationBean) initializer).getTargetBeanName();</span><br><span class="line">		addServletContextInitializerBean(Filter<span class="class">.<span class="keyword">class</span>, <span class="title">beanName</span>, <span class="title">initializer</span>, <span class="title">beanFactory</span>, <span class="title">source</span>)</span>;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">else</span> <span class="keyword">if</span> (initializer <span class="keyword">instanceof</span> ServletListenerRegistrationBean) &#123;</span><br><span class="line">		EventListener source = ((ServletListenerRegistrationBean&lt;?&gt;) initializer).getListener();</span><br><span class="line">		addServletContextInitializerBean(EventListener<span class="class">.<span class="keyword">class</span>, <span class="title">beanName</span>, <span class="title">initializer</span>, <span class="title">beanFactory</span>, <span class="title">source</span>)</span>;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">else</span> &#123;</span><br><span class="line">		addServletContextInitializerBean(ServletContextInitializer<span class="class">.<span class="keyword">class</span>, <span class="title">beanName</span>, <span class="title">initializer</span>, <span class="title">beanFactory</span>,</span></span><br><span class="line"><span class="class">				<span class="title">initializer</span>)</span>;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>getOrderedBeansOfType 方法便是去容器中寻找注册过得 ServletContextInitializer ，这时候就可以把之前那些 RegisterBean 全部加载出来了，并且 RegisterBean 还实现了 Ordered 接口，在这儿用于排序。addServletContextInitializerBean这个方法中我们便看到了要找的RegistrationBean。</p>
<h2 id="ServletWebServerApplicationContext-加载流程总结"><a href="#ServletWebServerApplicationContext-加载流程总结" class="headerlink" title="ServletWebServerApplicationContext 加载流程总结"></a>ServletWebServerApplicationContext 加载流程总结</h2><p>总结如下：</p>
<ul>
<li>ServletWebServerApplicationContext 的 onRefresh 方法触发配置了一个匿名的 ServletContextInitializer。</li>
<li>这个匿名的 ServletContextInitializer 的 onStartup 方法会去容器中搜索到了所有的 RegisterBean 并按照顺序加载到 ServletContext 中。</li>
<li>这个匿名的 ServletContextInitializer 最终传递给 TomcatStarter，由 TomcatStarter 的 onStartup 方法去触发 ServletContextInitializer 的 onStartup 方法，最终完成装配！</li>
</ul>
<p>看到这里ServletContextInitializer是怎么加载RegisterBean 我们搞明白了，但是还有一些疑问？TomcatServletWebServerFactory是哪里加载的？TomcatStarter是怎样创建的？</p>
<h3 id="TomcatServletWebServerFactory的加载"><a href="#TomcatServletWebServerFactory的加载" class="headerlink" title="TomcatServletWebServerFactory的加载"></a>TomcatServletWebServerFactory的加载</h3><p>TomcatServletWebServerFactory其实是通过springboot的自动装配加载的</p>
<p><img src="https://tu.dxysun.com/image-20210829155317658-20210829155322.png" alt="image-20210829155317658"></p>
<blockquote>
<p>红圈中还有我们熟悉的DispatcherServlet的自动装配</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span>(proxyBeanMethods = <span class="keyword">false</span>)</span><br><span class="line"><span class="meta">@AutoConfigureOrder</span>(Ordered.HIGHEST_PRECEDENCE)</span><br><span class="line"><span class="meta">@ConditionalOnClass</span>(ServletRequest<span class="class">.<span class="keyword">class</span>)</span></span><br><span class="line"><span class="class">@<span class="title">ConditionalOnWebApplication</span>(<span class="title">type</span> </span>= Type.SERVLET)</span><br><span class="line"><span class="meta">@EnableConfigurationProperties</span>(ServerProperties<span class="class">.<span class="keyword">class</span>)</span></span><br><span class="line"><span class="class">@<span class="title">Import</span>(</span>&#123; ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar<span class="class">.<span class="keyword">class</span>,</span></span><br><span class="line"><span class="class">		<span class="title">ServletWebServerFactoryConfiguration</span>.<span class="title">EmbeddedTomcat</span>.<span class="title">class</span>,</span></span><br><span class="line"><span class="class">		<span class="title">ServletWebServerFactoryConfiguration</span>.<span class="title">EmbeddedJetty</span>.<span class="title">class</span>,</span></span><br><span class="line"><span class="class">		<span class="title">ServletWebServerFactoryConfiguration</span>.<span class="title">EmbeddedUndertow</span>.<span class="title">class</span> &#125;)</span></span><br><span class="line"><span class="class"><span class="title">public</span> <span class="title">class</span> <span class="title">ServletWebServerFactoryAutoConfiguration</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Bean</span></span><br><span class="line">	<span class="function"><span class="keyword">public</span> ServletWebServerFactoryCustomizer <span class="title">servletWebServerFactoryCustomizer</span><span class="params">(ServerProperties serverProperties)</span> </span>&#123;</span><br><span class="line">		<span class="keyword">return</span> <span class="keyword">new</span> ServletWebServerFactoryCustomizer(serverProperties);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Bean</span></span><br><span class="line">	<span class="meta">@ConditionalOnClass</span>(name = <span class="string">"org.apache.catalina.startup.Tomcat"</span>)</span><br><span class="line">	<span class="function"><span class="keyword">public</span> TomcatServletWebServerFactoryCustomizer <span class="title">tomcatServletWebServerFactoryCustomizer</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">			ServerProperties serverProperties)</span> </span>&#123;</span><br><span class="line">		<span class="keyword">return</span> <span class="keyword">new</span> TomcatServletWebServerFactoryCustomizer(serverProperties);</span><br><span class="line">	&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@AutoConfigureOrder</span>(Ordered.HIGHEST_PRECEDENCE)</span><br><span class="line"><span class="meta">@Configuration</span>(proxyBeanMethods = <span class="keyword">false</span>)</span><br><span class="line"><span class="meta">@ConditionalOnWebApplication</span>(type = Type.SERVLET)</span><br><span class="line"><span class="meta">@ConditionalOnClass</span>(DispatcherServlet<span class="class">.<span class="keyword">class</span>)</span></span><br><span class="line"><span class="class">@<span class="title">AutoConfigureAfter</span>(<span class="title">ServletWebServerFactoryAutoConfiguration</span>.<span class="title">class</span>)</span></span><br><span class="line"><span class="class"><span class="title">public</span> <span class="title">class</span> <span class="title">DispatcherServletAutoConfiguration</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">	<span class="comment">/**</span></span><br><span class="line"><span class="comment">	 * The bean name for a DispatcherServlet that will be mapped to the root URL "/".</span></span><br><span class="line"><span class="comment">	 */</span></span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = <span class="string">"dispatcherServlet"</span>;</span><br><span class="line"></span><br><span class="line">	<span class="comment">/**</span></span><br><span class="line"><span class="comment">	 * The bean name for a ServletRegistrationBean for the DispatcherServlet "/".</span></span><br><span class="line"><span class="comment">	 */</span></span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = <span class="string">"dispatcherServletRegistration"</span>;</span><br><span class="line"></span><br><span class="line">	<span class="meta">@Configuration</span>(proxyBeanMethods = <span class="keyword">false</span>)</span><br><span class="line">	<span class="meta">@Conditional</span>(DefaultDispatcherServletCondition<span class="class">.<span class="keyword">class</span>)</span></span><br><span class="line"><span class="class">	@<span class="title">ConditionalOnClass</span>(<span class="title">ServletRegistration</span>.<span class="title">class</span>)</span></span><br><span class="line"><span class="class">	@<span class="title">EnableConfigurationProperties</span>(<span class="title">WebMvcProperties</span>.<span class="title">class</span>)</span></span><br><span class="line"><span class="class">	<span class="title">protected</span> <span class="title">static</span> <span class="title">class</span> <span class="title">DispatcherServletConfiguration</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">		<span class="meta">@Bean</span>(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)</span><br><span class="line">		<span class="function"><span class="keyword">public</span> DispatcherServlet <span class="title">dispatcherServlet</span><span class="params">(WebMvcProperties webMvcProperties)</span> </span>&#123;</span><br><span class="line">			DispatcherServlet dispatcherServlet = <span class="keyword">new</span> DispatcherServlet();</span><br><span class="line">			dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());</span><br><span class="line">			dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());</span><br><span class="line">			dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());</span><br><span class="line">			dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());</span><br><span class="line">			dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());</span><br><span class="line">			<span class="keyword">return</span> dispatcherServlet;</span><br><span class="line">		&#125;</span><br></pre></td></tr></table></figure>



<h3 id="TomcatStarter的创建"><a href="#TomcatStarter的创建" class="headerlink" title="TomcatStarter的创建"></a>TomcatStarter的创建</h3><p>TomcatStarter是被 new 出来的，当调用<code>TomcatServletWebServerFactory.getWebServer()</code>方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> WebServer <span class="title">getWebServer</span><span class="params">(ServletContextInitializer... initializers)</span> </span>&#123;</span><br><span class="line">	<span class="keyword">if</span> (<span class="keyword">this</span>.disableMBeanRegistry) &#123;</span><br><span class="line">		Registry.disableRegistry();</span><br><span class="line">	&#125;</span><br><span class="line">	Tomcat tomcat = <span class="keyword">new</span> Tomcat();</span><br><span class="line">	File baseDir = (<span class="keyword">this</span>.baseDirectory != <span class="keyword">null</span>) ? <span class="keyword">this</span>.baseDirectory : createTempDir(<span class="string">"tomcat"</span>);</span><br><span class="line">	tomcat.setBaseDir(baseDir.getAbsolutePath());</span><br><span class="line">	Connector connector = <span class="keyword">new</span> Connector(<span class="keyword">this</span>.protocol);</span><br><span class="line">	connector.setThrowOnFailure(<span class="keyword">true</span>);</span><br><span class="line">	tomcat.getService().addConnector(connector);</span><br><span class="line">	customizeConnector(connector);</span><br><span class="line">	tomcat.setConnector(connector);</span><br><span class="line">	tomcat.getHost().setAutoDeploy(<span class="keyword">false</span>);</span><br><span class="line">	configureEngine(tomcat.getEngine());</span><br><span class="line">	<span class="keyword">for</span> (Connector additionalConnector : <span class="keyword">this</span>.additionalTomcatConnectors) &#123;</span><br><span class="line">		tomcat.getService().addConnector(additionalConnector);</span><br><span class="line">	&#125;</span><br><span class="line">   <span class="comment">// 这个方法会调用configureContext方法</span></span><br><span class="line">	prepareContext(tomcat.getHost(), initializers);</span><br><span class="line">	<span class="keyword">return</span> getTomcatWebServer(tomcat);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">prepareContext</span><span class="params">(Host host, ServletContextInitializer[] initializers)</span> </span>&#123;</span><br><span class="line">	File documentRoot = getValidDocumentRoot();</span><br><span class="line">	TomcatEmbeddedContext context = <span class="keyword">new</span> TomcatEmbeddedContext();</span><br><span class="line">	<span class="keyword">if</span> (documentRoot != <span class="keyword">null</span>) &#123;</span><br><span class="line">		context.setResources(<span class="keyword">new</span> LoaderHidingResourceRoot(context));</span><br><span class="line">	&#125;</span><br><span class="line">	context.setName(getContextPath());</span><br><span class="line">	context.setDisplayName(getDisplayName());</span><br><span class="line">	context.setPath(getContextPath());</span><br><span class="line">	File docBase = (documentRoot != <span class="keyword">null</span>) ? documentRoot : createTempDir(<span class="string">"tomcat-docbase"</span>);</span><br><span class="line">	context.setDocBase(docBase.getAbsolutePath());</span><br><span class="line">	context.addLifecycleListener(<span class="keyword">new</span> FixContextListener());</span><br><span class="line">	context.setParentClassLoader((<span class="keyword">this</span>.resourceLoader != <span class="keyword">null</span>) ? <span class="keyword">this</span>.resourceLoader.getClassLoader()</span><br><span class="line">			: ClassUtils.getDefaultClassLoader());</span><br><span class="line">	resetDefaultLocaleMapping(context);</span><br><span class="line">	addLocaleMappings(context);</span><br><span class="line">	<span class="keyword">try</span> &#123;</span><br><span class="line">		context.setCreateUploadTargets(<span class="keyword">true</span>);</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">catch</span> (NoSuchMethodError ex) &#123;</span><br><span class="line">		<span class="comment">// Tomcat is &lt; 8.5.39. Continue.</span></span><br><span class="line">	&#125;</span><br><span class="line">	configureTldPatterns(context);</span><br><span class="line">	WebappLoader loader = <span class="keyword">new</span> WebappLoader();</span><br><span class="line">	loader.setLoaderClass(TomcatEmbeddedWebappClassLoader<span class="class">.<span class="keyword">class</span>.<span class="title">getName</span>())</span>;</span><br><span class="line">	loader.setDelegate(<span class="keyword">true</span>);</span><br><span class="line">	context.setLoader(loader);</span><br><span class="line">	<span class="keyword">if</span> (isRegisterDefaultServlet()) &#123;</span><br><span class="line">		addDefaultServlet(context);</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">if</span> (shouldRegisterJspServlet()) &#123;</span><br><span class="line">		addJspServlet(context);</span><br><span class="line">		addJasperInitializer(context);</span><br><span class="line">	&#125;</span><br><span class="line">	context.addLifecycleListener(<span class="keyword">new</span> StaticResourceConfigurer(context));</span><br><span class="line">   <span class="comment">// 在这里得到ServletContextInitializer数组</span></span><br><span class="line">	ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);</span><br><span class="line">	host.addChild(context);</span><br><span class="line">   <span class="comment">// TomcatStarter  在这个方法里被示例化出来</span></span><br><span class="line">	configureContext(context, initializersToUse);</span><br><span class="line">	postProcessContext(context);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>最后会调到TomcatServletWebServerFactory.configureContext ，TomcatStarter 是被主动实例化出来的，并且还传入了 ServletContextInitializer 的数组</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">configureContext</span><span class="params">(Context context, ServletContextInitializer[] initializers)</span> </span>&#123;</span><br><span class="line">   <span class="comment">// new 一个 TomcatStarter </span></span><br><span class="line">	TomcatStarter starter = <span class="keyword">new</span> TomcatStarter(initializers);</span><br><span class="line">	<span class="keyword">if</span> (context <span class="keyword">instanceof</span> TomcatEmbeddedContext) &#123;</span><br><span class="line">		TomcatEmbeddedContext embeddedContext = (TomcatEmbeddedContext) context;</span><br><span class="line">		embeddedContext.setStarter(starter);</span><br><span class="line">		embeddedContext.setFailCtxIfServletStartFails(<span class="keyword">true</span>);</span><br><span class="line">	&#125;</span><br><span class="line">	context.addServletContainerInitializer(starter, NO_CLASSES);</span><br><span class="line">	<span class="keyword">for</span> (LifecycleListener lifecycleListener : <span class="keyword">this</span>.contextLifecycleListeners) &#123;</span><br><span class="line">		context.addLifecycleListener(lifecycleListener);</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">for</span> (Valve valve : <span class="keyword">this</span>.contextValves) &#123;</span><br><span class="line">		context.getPipeline().addValve(valve);</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">for</span> (ErrorPage errorPage : getErrorPages()) &#123;</span><br><span class="line">		org.apache.tomcat.util.descriptor.web.ErrorPage tomcatErrorPage = <span class="keyword">new</span> org.apache.tomcat.util.descriptor.web.ErrorPage();</span><br><span class="line">		tomcatErrorPage.setLocation(errorPage.getPath());</span><br><span class="line">		tomcatErrorPage.setErrorCode(errorPage.getStatusCode());</span><br><span class="line">		tomcatErrorPage.setExceptionType(errorPage.getExceptionName());</span><br><span class="line">		context.addErrorPage(tomcatErrorPage);</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">for</span> (MimeMappings.Mapping mapping : getMimeMappings()) &#123;</span><br><span class="line">		context.addMimeMapping(mapping.getExtension(), mapping.getMimeType());</span><br><span class="line">	&#125;</span><br><span class="line">	configureSession(context);</span><br><span class="line">	<span class="keyword">new</span> DisableReferenceClearingContextCustomizer().customize(context);</span><br><span class="line">	<span class="keyword">for</span> (TomcatContextCustomizer customizer : <span class="keyword">this</span>.tomcatContextCustomizers) &#123;</span><br><span class="line">		customizer.customize(context);</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>mergeInitializers</code>，会添加三个 ServletContextInitializer，包含了 ServletWebServerApplicationContext 中的匿名实现。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">protected</span> <span class="keyword">final</span> ServletContextInitializer[] mergeInitializers(ServletContextInitializer... initializers) &#123;</span><br><span class="line">	List&lt;ServletContextInitializer&gt; mergedInitializers = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line">	mergedInitializers.add((servletContext) -&gt; <span class="keyword">this</span>.initParameters.forEach(servletContext::setInitParameter));</span><br><span class="line">	mergedInitializers.add(<span class="keyword">new</span> SessionConfiguringInitializer(<span class="keyword">this</span>.session));</span><br><span class="line">	mergedInitializers.addAll(Arrays.asList(initializers));</span><br><span class="line">	mergedInitializers.addAll(<span class="keyword">this</span>.initializers);</span><br><span class="line">	<span class="keyword">return</span> mergedInitializers.toArray(<span class="keyword">new</span> ServletContextInitializer[<span class="number">0</span>]);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>至此，springboot中springMVC的加载分析完成</p>
<h1 id="第三种注册-Servlet-的方式"><a href="#第三种注册-Servlet-的方式" class="headerlink" title="第三种注册 Servlet 的方式"></a>第三种注册 Servlet 的方式</h1><p>研究完了上述 springboot 启动的内部原理，可以发现 ServletContextInitializer 其实是 spring 中 ServletContainerInitializer 的代理，虽然 springboot 中 Servlet3.0 不起作用了，但它的代理还是会被加载的，于是我们有了第三种方式注册 servlet。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">CustomServletContextInitializer</span> <span class="keyword">implements</span> <span class="title">ServletContextInitializer</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">static</span> String JAR_HELLO_URL = <span class="string">"/hello"</span>;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onStartup</span><span class="params">(ServletContext servletContext)</span> <span class="keyword">throws</span> ServletException </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"创建 helloWorldServlet..."</span>);</span><br><span class="line"></span><br><span class="line">        ServletRegistration.Dynamic servlet = servletContext.addServlet(</span><br><span class="line">                HelloWorldServlet<span class="class">.<span class="keyword">class</span>.<span class="title">getSimpleName</span>(),</span></span><br><span class="line"><span class="class">                <span class="title">HelloWorldServlet</span>.<span class="title">class</span>)</span>;</span><br><span class="line">        servlet.addMapping(JAR_HELLO_URL);</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">"创建 helloWorldFilter..."</span>);</span><br><span class="line"></span><br><span class="line">        FilterRegistration.Dynamic filter = servletContext.addFilter(</span><br><span class="line">                HelloWorldFilter<span class="class">.<span class="keyword">class</span>.<span class="title">getSimpleName</span>(), <span class="title">HelloWorldFilter</span>.<span class="title">class</span>)</span>;</span><br><span class="line"></span><br><span class="line">        EnumSet&lt;DispatcherType&gt; dispatcherTypes = EnumSet.allOf(DispatcherType<span class="class">.<span class="keyword">class</span>)</span>;</span><br><span class="line">        dispatcherTypes.add(DispatcherType.REQUEST);</span><br><span class="line">        dispatcherTypes.add(DispatcherType.FORWARD);</span><br><span class="line"></span><br><span class="line">        filter.addMappingForUrlPatterns(dispatcherTypes, <span class="keyword">true</span>, JAR_HELLO_URL);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><p><a href="https://www.cnkirito.moe/servlet-explore/" target="_blank" rel="noopener">Spring 揭秘 – 寻找遗失的 web.xml</a></p>
 
      <!-- reward -->
      
      <div id="reword-out">
        <div id="reward-btn">
          打赏
        </div>
      </div>
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
<div class="share-btn">
      <span class="share-sns share-outer">
        <i class="ri-share-forward-line"></i>
        分享
      </span>
      <div class="share-wrap">
        <i class="arrow"></i>
        <div class="share-icons">
          
          <a class="weibo share-sns" href="javascript:;" data-type="weibo">
            <i class="ri-weibo-fill"></i>
          </a>
          <a class="weixin share-sns wxFab" href="javascript:;" data-type="weixin">
            <i class="ri-wechat-fill"></i>
          </a>
          <a class="qq share-sns" href="javascript:;" data-type="qq">
            <i class="ri-qq-fill"></i>
          </a>
          <a class="douban share-sns" href="javascript:;" data-type="douban">
            <i class="ri-douban-line"></i>
          </a>
          <!-- <a class="qzone share-sns" href="javascript:;" data-type="qzone">
            <i class="icon icon-qzone"></i>
          </a> -->
          
          <a class="facebook share-sns" href="javascript:;" data-type="facebook">
            <i class="ri-facebook-circle-fill"></i>
          </a>
          <a class="twitter share-sns" href="javascript:;" data-type="twitter">
            <i class="ri-twitter-fill"></i>
          </a>
          <a class="google share-sns" href="javascript:;" data-type="google">
            <i class="ri-google-fill"></i>
          </a>
        </div>
      </div>
</div>

<div class="wx-share-modal">
    <a class="modal-close" href="javascript:;"><i class="ri-close-circle-line"></i></a>
    <p>扫一扫，分享到微信</p>
    <div class="wx-qrcode">
      <img src="//api.qrserver.com/v1/create-qr-code/?size=150x150&data=https://dxysun.com/2021/08/29/springmvcForSpringboot/" alt="微信分享二维码">
    </div>
</div>

<div id="share-mask"></div>  
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/spring/" rel="tag">spring</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/springboot/" rel="tag">springboot</a></li></ul>

    </footer>
  </div>

   
  <nav class="article-nav">
    
      <a href="/2021/08/29/springForImport/" class="article-nav-link">
        <strong class="article-nav-caption">上一篇</strong>
        <div class="article-nav-title">
          
            Spring中@Import注解的使用和实现原理
          
        </div>
      </a>
    
    
      <a href="/2021/08/28/zookeeperForPrinciple2/" class="article-nav-link">
        <strong class="article-nav-caption">下一篇</strong>
        <div class="article-nav-title">基于Zookeeper实现分布式锁及Leader选举</div>
      </a>
    
  </nav>

  
   
  
</article>

</section>
      <footer class="footer">
  <div class="outer">
    <ul>
      <li>
        Copyrights &copy;
        2015-2024
        <i class="ri-heart-fill heart_icon"></i> dxysun
      </li>
    </ul>
    <ul>
      <li>
        
        
        
        由 <a href="https://hexo.io" target="_blank">Hexo</a> 强力驱动
        <span class="division">|</span>
        主题 - <a href="https://github.com/Shen-Yu/hexo-theme-ayer" target="_blank">Ayer</a>
        
      </li>
    </ul>
    <ul>
      <li>
        
        
        <span>
  <span><i class="ri-user-3-fill"></i>访问人数:<span id="busuanzi_value_site_uv"></span></s>
  <span class="division">|</span>
  <span><i class="ri-eye-fill"></i>浏览次数:<span id="busuanzi_value_page_pv"></span></span>
</span>
        
      </li>
    </ul>
    <ul>
      
        <li>
          <a href="https://beian.miit.gov.cn" target="_black" rel="nofollow">豫ICP备17012675号-1</a>
        </li>
        
    </ul>
    <ul>
      
    </ul>
    <ul>
      <li>
        <!-- cnzz统计 -->
        
      </li>
    </ul>
  </div>
</footer>
      <div class="float_btns">
        <div class="totop" id="totop">
  <i class="ri-arrow-up-line"></i>
</div>

<div class="todark" id="todark">
  <i class="ri-moon-line"></i>
</div>

      </div>
    </main>
    <aside class="sidebar on">
      <button class="navbar-toggle"></button>
<nav class="navbar">
  
  <div class="logo">
    <a href="/"><img src="https://dxysun.com/static/logo.png" alt="迎着朝阳"></a>
  </div>
  
  <ul class="nav nav-main">
    
    <li class="nav-item">
      <a class="nav-item-link" href="/">主页</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/archives">归档</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/categories">分类</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags">标签</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/photos">相册</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/friends">友链</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/about">关于我</a>
    </li>
    
  </ul>
</nav>
<nav class="navbar navbar-bottom">
  <ul class="nav">
    <li class="nav-item">
      
      <a class="nav-item-link nav-item-search"  title="搜索">
        <i class="ri-search-line"></i>
      </a>
      
      
      <a class="nav-item-link" target="_blank" href="/atom.xml" title="RSS Feed">
        <i class="ri-rss-line"></i>
      </a>
      
    </li>
  </ul>
</nav>
<div class="search-form-wrap">
  <div class="local-search local-search-plugin">
  <input type="search" id="local-search-input" class="local-search-input" placeholder="Search...">
  <div id="local-search-result" class="local-search-result"></div>
</div>
</div>
    </aside>
    <script>
      if (window.matchMedia("(max-width: 768px)").matches) {
        document.querySelector('.content').classList.remove('on');
        document.querySelector('.sidebar').classList.remove('on');
      }
    </script>
    <div id="mask"></div>

<!-- #reward -->
<div id="reward">
  <span class="close"><i class="ri-close-line"></i></span>
  <p class="reward-p"><i class="ri-cup-line"></i>请我喝杯咖啡吧~</p>
  <div class="reward-box">
    
    <div class="reward-item">
      <img class="reward-img" src="https://tu.dxysun.com/alipay-20201219151322.jpg">
      <span class="reward-type">支付宝</span>
    </div>
    
    
    <div class="reward-item">
      <img class="reward-img" src="https://tu.dxysun.com/weixin-20201219151346.png">
      <span class="reward-type">微信</span>
    </div>
    
  </div>
</div>
    
<script src="/js/jquery-2.0.3.min.js"></script>


<script src="/js/lazyload.min.js"></script>

<!-- Tocbot -->


<script src="/js/tocbot.min.js"></script>

<script>
  tocbot.init({
    tocSelector: '.tocbot',
    contentSelector: '.article-entry',
    headingSelector: 'h1, h2, h3, h4, h5, h6',
    hasInnerContainers: true,
    scrollSmooth: true,
    scrollContainer: 'main',
    positionFixedSelector: '.tocbot',
    positionFixedClass: 'is-position-fixed',
    fixedSidebarOffset: 'auto'
  });
</script>

<script src="https://cdn.jsdelivr.net/npm/jquery-modal@0.9.2/jquery.modal.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/jquery-modal@0.9.2/jquery.modal.min.css">
<script src="https://cdn.jsdelivr.net/npm/justifiedGallery@3.7.0/dist/js/jquery.justifiedGallery.min.js"></script>

<script src="/dist/main.js"></script>

<!-- ImageViewer -->

<!-- Root element of PhotoSwipe. Must have class pswp. -->
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">

    <!-- Background of PhotoSwipe. 
         It's a separate element as animating opacity is faster than rgba(). -->
    <div class="pswp__bg"></div>

    <!-- Slides wrapper with overflow:hidden. -->
    <div class="pswp__scroll-wrap">

        <!-- Container that holds slides. 
            PhotoSwipe keeps only 3 of them in the DOM to save memory.
            Don't modify these 3 pswp__item elements, data is added later on. -->
        <div class="pswp__container">
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
        </div>

        <!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
        <div class="pswp__ui pswp__ui--hidden">

            <div class="pswp__top-bar">

                <!--  Controls are self-explanatory. Order can be changed. -->

                <div class="pswp__counter"></div>

                <button class="pswp__button pswp__button--close" title="Close (Esc)"></button>

                <button class="pswp__button pswp__button--share" style="display:none" title="Share"></button>

                <button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>

                <button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>

                <!-- Preloader demo http://codepen.io/dimsemenov/pen/yyBWoR -->
                <!-- element will get class pswp__preloader--active when preloader is running -->
                <div class="pswp__preloader">
                    <div class="pswp__preloader__icn">
                        <div class="pswp__preloader__cut">
                            <div class="pswp__preloader__donut"></div>
                        </div>
                    </div>
                </div>
            </div>

            <div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
                <div class="pswp__share-tooltip"></div>
            </div>

            <button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
            </button>

            <button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
            </button>

            <div class="pswp__caption">
                <div class="pswp__caption__center"></div>
            </div>

        </div>

    </div>

</div>

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/default-skin/default-skin.min.css">
<script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe-ui-default.min.js"></script>

<script>
    function viewer_init() {
        let pswpElement = document.querySelectorAll('.pswp')[0];
        let $imgArr = document.querySelectorAll(('.article-entry img:not(.reward-img)'))

        $imgArr.forEach(($em, i) => {
            $em.onclick = () => {
                // slider展开状态
                // todo: 这样不好，后面改成状态
                if (document.querySelector('.left-col.show')) return
                let items = []
                $imgArr.forEach(($em2, i2) => {
                    let img = $em2.getAttribute('data-idx', i2)
                    let src = $em2.getAttribute('data-target') || $em2.getAttribute('src')
                    let title = $em2.getAttribute('alt')
                    // 获得原图尺寸
                    const image = new Image()
                    image.src = src
                    items.push({
                        src: src,
                        w: image.width || $em2.width,
                        h: image.height || $em2.height,
                        title: title
                    })
                })
                var gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, {
                    index: parseInt(i)
                });
                gallery.init()
            }
        })
    }
    viewer_init()
</script>

<!-- MathJax -->

<script type="text/x-mathjax-config">
  MathJax.Hub.Config({
      tex2jax: {
          inlineMath: [ ['$','$'], ["\\(","\\)"]  ],
          processEscapes: true,
          skipTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code']
      }
  });

  MathJax.Hub.Queue(function() {
      var all = MathJax.Hub.getAllJax(), i;
      for(i=0; i < all.length; i += 1) {
          all[i].SourceElement().parentNode.className += ' has-jax';
      }
  });
</script>

<script src="https://cdn.jsdelivr.net/npm/mathjax@2.7.6/unpacked/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script>
  var ayerConfig = {
    mathjax: true
  }
</script>

<!-- Katex -->

<!-- busuanzi  -->


<script src="/js/busuanzi-2.3.pure.min.js"></script>


<!-- ClickLove -->

<!-- ClickBoom1 -->

<!-- ClickBoom2 -->

<!-- CodeCopy -->


<link rel="stylesheet" href="/css/clipboard.css">

<script src="https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js"></script>
<script>
  function wait(callback, seconds) {
    var timelag = null;
    timelag = window.setTimeout(callback, seconds);
  }
  !function (e, t, a) {
    var initCopyCode = function(){
      var copyHtml = '';
      copyHtml += '<button class="btn-copy" data-clipboard-snippet="">';
      copyHtml += '<i class="ri-file-copy-2-line"></i><span>COPY</span>';
      copyHtml += '</button>';
      $(".highlight .code pre").before(copyHtml);
      $(".article pre code").before(copyHtml);
      var clipboard = new ClipboardJS('.btn-copy', {
        target: function(trigger) {
          return trigger.nextElementSibling;
        }
      });
      clipboard.on('success', function(e) {
        let $btn = $(e.trigger);
        $btn.addClass('copied');
        let $icon = $($btn.find('i'));
        $icon.removeClass('ri-file-copy-2-line');
        $icon.addClass('ri-checkbox-circle-line');
        let $span = $($btn.find('span'));
        $span[0].innerText = 'COPIED';
        
        wait(function () { // 等待两秒钟后恢复
          $icon.removeClass('ri-checkbox-circle-line');
          $icon.addClass('ri-file-copy-2-line');
          $span[0].innerText = 'COPY';
        }, 2000);
      });
      clipboard.on('error', function(e) {
        e.clearSelection();
        let $btn = $(e.trigger);
        $btn.addClass('copy-failed');
        let $icon = $($btn.find('i'));
        $icon.removeClass('ri-file-copy-2-line');
        $icon.addClass('ri-time-line');
        let $span = $($btn.find('span'));
        $span[0].innerText = 'COPY FAILED';
        
        wait(function () { // 等待两秒钟后恢复
          $icon.removeClass('ri-time-line');
          $icon.addClass('ri-file-copy-2-line');
          $span[0].innerText = 'COPY';
        }, 2000);
      });
    }
    initCopyCode();
  }(window, document);
</script>


<!-- CanvasBackground -->


<script src="/js/dz.js"></script>



    
  </div>
</body>

</html>